package com.ming_za.odupdaterserver.core.service.res;

import com.ming_za.odupdaterserver.api.LabelManager;
import com.ming_za.odupdaterserver.api.MainVersionManager;
import com.ming_za.odupdaterserver.api.ResourcesManager;
import com.ming_za.odupdaterserver.core.config.ConfigManager;
import com.ming_za.odupdaterserver.core.config.type.IntValue;
import com.ming_za.odupdaterserver.core.exception.BusinessException;
import com.ming_za.odupdaterserver.core.exception.ROEnum;
import com.ming_za.odupdaterserver.core.service.ReturnBusinessPackage;
import com.ming_za.odupdaterserver.core.service.res.cache.MainVersionCacheOptions;
import com.ming_za.odupdaterserver.core.service.res.obj.MainUpdateItem;
import com.ming_za.odupdaterserver.core.service.res.obj.MainUpdateItemV1_1;
import com.ming_za.odupdaterserver.core.service.res.obj.VersionItem;
import com.ming_za.odupdaterserver.core.sqlite.domain.MainVersion;
import com.ming_za.odupdaterserver.core.sqlite.domain.Res;
import com.ming_za.odupdaterserver.core.thread.BlockExecuteTreadPool;
import com.ming_za.odupdaterserver.core.utils.Msg;
import com.ming_za.odupdaterserver.core.utils.cache.MapInternalCache;
import com.ming_za.odupdaterserver.core.vo.resp.VersionInfoResp;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 主版本管理器,接口实现类
 * <p>负责对版本资源统一管理</p>
 * <p>如需调用更新功能请查看{@link com.ming_za.odupdaterserver.api.Updater}</p>
 * <p>如需调用资源功能请查看{@link ResourcesManager}</p>
 */
@Service
public class MainVersionManagerImpl implements MainVersionManager {

    private final LabelManager labelManager;
    private final ResourcesManager resourcesManager;
    private final Msg msg;

    /**
     * 主版本更新映射缓存
     * <p>主版本：更新资源列表</p>
     * <p>从小到大排序</p>
     */
    private final MapInternalCache<Integer, MainVersion> mainVersionCrash;

    public MainVersionManagerImpl(LabelManager labelManager, ResourcesManager resourcesManager, Msg msg, MainVersionCacheOptions mainVersionCacheOptions) {
        this.labelManager = labelManager;
        this.resourcesManager = resourcesManager;
        this.msg = msg;
        mainVersionCrash = new MapInternalCache<>(mainVersionCacheOptions);

        Map<Integer, MainVersion> cacheData = mainVersionCrash.getCacheData();
        Integer v;
        if (!cacheData.isEmpty() && !cacheData.get((v = cacheData.keySet().iterator().next())).isPublished()){
            unpublishedVersion = v;
        }
        Integer latestPublishedVersion;
        if (cacheData.size() > 1){
            latestPublishedVersion = cacheData.keySet().toArray(Integer[]::new)[1];
            if ((unpublishedVersion ^ latestPublishedVersion) > 0x00010000){
                unpublishedNecessary = true;
            }
        }else if (cacheData.size() == 1){
            if (unpublishedVersion >= 0x00010000){
                unpublishedNecessary = true;
            }
        }
    }

    /**
     * <p>通过{@link LabelManager}获取 label 二进制位</p>
     * <p>从缓存中获取存在label的元素返回</p>
     */
    @Override
    public ReturnBusinessPackage<List<MainUpdateItem>> getMainUpdateItems(Integer version, Long label, boolean published,int clientVersion) {

        long lb = labelManager.getLabelCompoundBit(label);

        if (lb == -1){
//            throw new BusinessException(ROEnum.UPDATE_NOTFOUND_LABEL);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_NOTFOUND_LABEL);
        }

        List<MainUpdateItem> resultItems = new ArrayList<>();

        MainVersion mv = getMainVersionCrash(version);

        if (mv != null && (mv.isPublished() || !published)){
            List<Res> items = resourcesManager.getRes(mv.getRes().toArray(new Long[0]));
            //当标签为默认时（0），则只能获取到==0的标签，两个for为了避免每次遍历都判断一次标签是否==0的局部固定结果
            if (lb != 0){
                for (Res item : items){
                    if ((item.getLabel() & lb) > 0){
                        if (clientVersion >= 0x01010000){
                            //支持MC mod
                            resultItems.add(new MainUpdateItemV1_1(item));
                        }else {
                            resultItems.add(new MainUpdateItem(item));
                        }
                    }
                }
            }else {
                for (Res item : items){
                    if (item.getLabel() == 0){
                        if (clientVersion >= 0x01010000){
                            //支持MC mod
                            resultItems.add(new MainUpdateItemV1_1(item));
                        }else {
                            resultItems.add(new MainUpdateItem(item));
                        }
                    }
                }
            }
        }

//        return resultItems;
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS,resultItems);
    }

    @Override
    public ReturnBusinessPackage<List<MainUpdateItem>> getVersionResAll(Integer version){
        MainVersion mainVersion = mainVersionCrash.getValue(version);
        if (mainVersion == null){
//            throw new BusinessException(ROEnum.UPDATE_NOTFOUND_VERSION);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_NOTFOUND_VERSION);
        }

        List<Res> res = resourcesManager.getRes(mainVersion.getRes().toArray(new Long[0]));

        List<MainUpdateItem> result = new ArrayList<>();
        for (Res item : res){
            result.add(new MainUpdateItem(item));
        }
//        return result;
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS,result);
    }

    @Override
    public Map<Integer, MainVersion> getMainUpdateItems() {
        return mainVersionCrash.getCacheData();
    }

    @Override
    public List<Integer> getVersions(Integer version,boolean published) {
        Map<Integer, MainVersion> c = mainVersionCrash.getCacheData();

        List<Integer> result = new ArrayList<>();

        if (!published){
            //非发布内容
            for (Map.Entry<Integer, MainVersion> entry : c.entrySet()){
                if (entry.getKey() > version){
                    result.add(entry.getKey());
                }
            }
        }else {
            //发布内容
            for (Map.Entry<Integer, MainVersion> entry : c.entrySet()){
                if (entry.getKey() > version && entry.getValue().isPublished()){
                    result.add(entry.getKey());
                }
            }
        }
        return result;
    }

    public List<VersionInfoResp> getAllVersionInfo(){
        List<VersionInfoResp> result = new ArrayList<>();
        for (Map.Entry<Integer, MainVersion> entry : getMainUpdateItems().entrySet()){
            MainVersion value = entry.getValue();
            result.add(new VersionInfoResp(entry.getKey(),value.getTime(),value.isPublished()));
        }
        return result;
    }

    @Override
    public List<Integer> getAllVersions() {
        return mainVersionCrash.getCacheData().keySet().stream().toList();
    }

    private volatile Integer unpublishedVersion = 0;
    private volatile boolean unpublishedNecessary = false;

    private final ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public ReturnBusinessPackage<Void> addUnpublishedRes(Long resId,boolean isNecessary) {
        reentrantLock.lock();
        try {
            if (unpublishedVersion == 0){
                generateNewVersionAndUpdate(resId,isNecessary);
            }else {

                //根据是否必须更新决定是否需要调整版本号
                if (isNecessary && !unpublishedNecessary){
                    ReturnBusinessPackage<Void> rbp = modifyVersion(unpublishedVersion + 0x00010000);
                    if (!rbp.isSuccess()){
                        return rbp;
                    }
                }

                List<Long> ids = mainVersionCrash.getValue(unpublishedVersion).getRes();

                //判断是否存在该资源
                for (Long id : ids){
                    if (id.equals(resId)){
//                        throw new BusinessException(ROEnum.UPDATE_RES_EXISTS);
                        return new ReturnBusinessPackage<>(ROEnum.UPDATE_RES_EXISTS);
                    }
                }

                ids.add(resId);
                mainVersionCrash.save(unpublishedVersion);
            }
        } finally {
            reentrantLock.unlock();
        }

        return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
    }

    @Override
    public ReturnBusinessPackage<Void> newVersion(boolean isNecessary) {
        reentrantLock.lock();
        try {
            if (unpublishedVersion == 0){
                generateNewVersionAndUpdate(isNecessary);
            }else {
                return new ReturnBusinessPackage<>(ROEnum.UPDATE_EXIST_UNPUBLISHED_VERSION);
            }
        } finally {
            reentrantLock.unlock();
        }

        return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
    }

    //没有未发布版时调用
    private void generateNewVersionAndUpdate(Long resId, boolean isNecessary){
        if (unpublishedVersion != 0){
            return;
        }
        Map<Integer, MainVersion> cacheData = mainVersionCrash.getCacheData();

        int newVersion;

        if (cacheData.isEmpty()){
            //当没有任何版本时执行
            newVersion = 0x01000000;
            unpublishedNecessary = isNecessary;
        }else {
            //获取最后一个版本，之后按照是否必须更新累加相应版本号
            //如果当前位版本号用完，则向下一位进一。具体版本耗尽情况由前端进行判断
            Integer latestVersion = cacheData.keySet().toArray(Integer[]::new)[0];

            if (isNecessary){
                newVersion = latestVersion + 0x00010000;
                unpublishedNecessary = true;
            }else {
                newVersion = latestVersion + 0x00000100;
                unpublishedNecessary = false;
            }
        }

        List<Long> resList = new ArrayList<>();
        resList.add(resId);
        mainVersionCrash.putValue(newVersion,new MainVersion(newVersion,resList,new Date(System.currentTimeMillis()),false));
        unpublishedVersion = newVersion;
    }

    //没有未发布版时调用
    private void generateNewVersionAndUpdate(boolean isNecessary){

        Map<Integer, MainVersion> cacheData = mainVersionCrash.getCacheData();

        int newVersion;

        if (cacheData.isEmpty()){
            //当没有任何版本时执行
            newVersion = 0x01000000;
            unpublishedNecessary = isNecessary;
        }else {
            //获取最后一个版本，之后按照是否必须更新累加相应版本号
            //如果当前位版本号用完，则向下一位进一。具体版本耗尽情况由前端进行判断
            Integer latestVersion = cacheData.keySet().toArray(Integer[]::new)[0];

            if (isNecessary){
                newVersion = latestVersion + 0x00010000;
                unpublishedNecessary = true;
            }else {
                newVersion = latestVersion + 0x00000100;
                unpublishedNecessary = false;
            }
        }

        List<Long> resList = new ArrayList<>();
        mainVersionCrash.putValue(newVersion,new MainVersion(newVersion,resList,new Date(System.currentTimeMillis()),false));
        unpublishedVersion = newVersion;
    }

    @Override
    public synchronized ReturnBusinessPackage<Object> publishVersion() {
        if (unpublishedVersion == 0){
//            throw new BusinessException(ROEnum.UPDATE_UNKNOWN_UNPUBLISHED_VERSION);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_UNKNOWN_UNPUBLISHED_VERSION);
        }

        int v = unpublishedVersion;

        MainVersion version = mainVersionCrash.getValue(unpublishedVersion);

        Set<Res> cs = new HashSet<>();
        List<String> repeatFile = new ArrayList<>();

        for (Res item : resourcesManager.getRes(version.getRes().toArray(new Long[0]))){

            for (Res c :cs){
                if (c.getClientPath().equals(item.getClientPath()) && (item.getLabel().equals(c.getLabel()) || (item.getLabel() & c.getLabel()) > 0)){
                    repeatFile.add(c.getClientPath());
                }
            }
            cs.add(item);
        }

//        System.out.println(repeatFile);
        if (!repeatFile.isEmpty()){
//            throw new BusinessException(ROEnum.UPDATE_CONFLICT_PATH_RES,repeatFile.toArray(new String[0]));
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_CONFLICT_PATH_RES,repeatFile.toArray(new String[0]));
        }

        version.setPublished(true);
        mainVersionCrash.save(unpublishedVersion);
        unpublishedVersion = 0;
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS,v);
    }

    @Override
    public synchronized ReturnBusinessPackage<Void> modifyVersion(Integer integer) {
        if (unpublishedVersion == 0){
//            throw new BusinessException(ROEnum.UPDATE_UNKNOWN_UNPUBLISHED_VERSION,"已发布版本不可修改版本号");
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_UNKNOWN_UNPUBLISHED_VERSION.setMsg("已发布版本不可修改版本号"));
        }
        MainVersion value = mainVersionCrash.getValue(unpublishedVersion);

        Map<Integer, MainVersion> cacheData = mainVersionCrash.getCacheData();
        if (cacheData.size() > 1){
            Integer latestPublishedVersion = cacheData.keySet().toArray(Integer[]::new)[1];
            if (integer <= latestPublishedVersion){
//                throw new BusinessException(ROEnum.UPDATE_VERSION_MUST_BIGGER_THAN_OLD);
                return new ReturnBusinessPackage<>(ROEnum.UPDATE_VERSION_MUST_BIGGER_THAN_OLD);
            }
            if ((integer ^ latestPublishedVersion) > 0x00010000){
                unpublishedNecessary = true;
            }
        }else {
            unpublishedNecessary = true;
            if (integer < 0x00000100){
//                throw new BusinessException(ROEnum.UPDATE_VERSION_MUST_BIGGER_THAN_OLD,"版本号最小为0.0.1");
                return new ReturnBusinessPackage<>(ROEnum.UPDATE_VERSION_MUST_BIGGER_THAN_OLD.setMsg("版本号最小为0.0.1"));
            }
        }
        mainVersionCrash.removeValue(unpublishedVersion);

        value.setVersion(integer);
        mainVersionCrash.putValue(integer,value);

        unpublishedVersion = integer;
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
    }

    @Override
    public ReturnBusinessPackage<Void> removeVersion(Integer integer) {

        MainVersion value = mainVersionCrash.getValue(integer);
        if (value == null){
//            throw new BusinessException(ROEnum.UPDATE_NOTFOUND_VERSION);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_NOTFOUND_VERSION);
        }

        try {
            resourcesManager.removeRes(value.getRes().toArray(new Long[0]));
            mainVersionCrash.removeValue(integer);
        } catch (Exception e) {
            msg.error(e.getMessage(),e);
            throw new BusinessException(e.getMessage(),e,ROEnum.UNEXPECTED_ERROR);
        }

        if (integer.equals(unpublishedVersion)){
            unpublishedVersion = 0;
        }

        return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
    }

    @Override
    public ReturnBusinessPackage<Void> removeVersionRes(Long[] resId,Integer version) {

        if (resId.length < 1){
            return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
        }

        if (version == null){
            version = getVersionOfRes(resId[0]);
        }

        MainVersion value = mainVersionCrash.getValue(version);
        if (value == null){
//            throw new BusinessException(ROEnum.UPDATE_NOTFOUND_VERSION);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_NOTFOUND_VERSION);
        }

        value.getRes().removeIf(item -> {
            for (Long aLong : resId) {
                if (item.equals(aLong)) {
                    return true;
                }
            }
            return false;
        });

        mainVersionCrash.save(version);
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
    }

    @Override
    public Integer getVersionOfRes(Long resId){

        Map<Integer, MainVersion> cacheData = mainVersionCrash.getCacheData();

        for (Map.Entry<Integer, MainVersion> entry : cacheData.entrySet()){
            List<Long> resIds = entry.getValue().getRes();
            for (Long id : resIds){
                if (id.equals(resId)){
                    return entry.getKey();
                }
            }
        }

        return 0;
    }

    public MainVersion getMainVersionCrash(Integer version) {

        return mainVersionCrash.getValue(version);
    }

}
